﻿using System;
using System.Linq;
using System.Text;
using KylinORM.Data;
using System.Reflection;
using System.Collections.Specialized;
using KylinORM.Utils;
using System.Collections.Generic;
using KylinORM.Mapper.Base;
using KylinORM.Aspect.Definition;
using KylinORM.CQRS;
using KylinORM.CQRS.Publisher;

namespace KylinORM.Mapper
{
    /// <summary>
    /// 支持事件的实体映射器
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class AspectMapper<T> : SimpleMapper<T>, IAspectMapper<T> where T : new()
    {
        #region 内部属性

        private IEndpoint eventBus = MessageBus.NewEndpoint();
        /// <summary>
        /// 创建指定实体类型的映射器
        /// </summary>
        protected internal AspectMapper()
            : base()
        {
        }
        /// <summary>
        /// 创建指定实体的映射器
        /// </summary>
        /// <param name="targetEntity">数据实体</param>
        /// <param name="definitionRule">实体对应映射规则</param>
        protected internal AspectMapper(T targetEntity, IEntityDefinitionRule definitionRule)
            : base(targetEntity, definitionRule)
        {
        }
        #endregion

        #region IAspectMapper<T> 成员
        /// <summary>
        /// 注册命令的处理程序
        /// </summary>
        /// <param name="messageName">命令名称</param>
        /// <param name="messageHandler">命令处理程序</param>
        public void On(string messageName, Action<IMessage> messageHandler)
        {
            this.eventBus.On(messageName, messageHandler);
        }
        /// <summary>
        /// 将命令发送到已注册的处理程序
        /// </summary>
        /// <param name="message">命令对象</param>
        public void Send(IMessage message)
        {
            this.eventBus.Send(message);
        }
        /// <summary>
        /// 在实体操作执行前调用指定委托
        /// </summary>
        /// <param name="eventName">事件类型</param>
        /// <param name="eventHandler">事件委托</param>
        /// <returns></returns>
        public AspectMapper<T> Before(MapperEvent eventName, Action<BeforeSubmitEvent> eventHandler)
        {
            Action<BeforeSubmitEvent> action = (BeforeSubmitEvent cmd) => eventHandler(cmd);
            eventBus.On("BeforeSubmitCommand_" + eventName, action);
            return this;
        }
        /// <summary>
        /// 在实体操作完成后调用指定委托
        /// </summary>
        /// <param name="eventName">事件类型</param>
        /// <param name="eventHandler">事件委托</param>
        /// <returns></returns>
        public AspectMapper<T> AfterReturning(MapperEvent eventName, Action<AfterReturningEvent> eventHandler)
        {
            Action<AfterReturningEvent> action = (AfterReturningEvent cmd) => eventHandler(cmd);
            eventBus.On("AfterReturningCommand_" + eventName, action);
            return this;
        }
        /// <summary>
        /// 在实体操作异常时挂起异常并调用指定委托
        /// </summary>
        /// <param name="eventName">事件类型</param>
        /// <param name="eventHandler">事件委托</param>
        /// <returns></returns>
        public AspectMapper<T> AfterThrowing(MapperEvent eventName, Action<AfterThrowingEvent> eventHandler)
        {
            Action<AfterThrowingEvent> action = (AfterThrowingEvent cmd) => eventHandler(cmd);
            eventBus.On("AfterThrowingCommand_" + eventName, action);
            return this;
        }
        /// <summary>
        /// 在实体操作返回时执行调用委托
        /// </summary>
        /// <param name="eventName">事件类型</param>
        /// <param name="eventHandler">事件委托</param>
        /// <returns></returns>
        public AspectMapper<T> AfterFinally(MapperEvent eventName, Action<AfterFinallyEvent> eventHandler)
        {
            Action<AfterFinallyEvent> action = (AfterFinallyEvent cmd) => eventHandler(cmd);
            eventBus.On("AfterFinallyCommand_" + eventName, action);
            return this;
        }
        #endregion

        #region 获取事件参数
        /// <summary>
        /// 获取事件所需参数
        /// </summary>
        /// <param name="keys">主键值</param>
        /// <param name="values">非主键值</param>
        protected void GetEntityKeyValues(out ReadonlyOrderedDictionary keys, out ReadonlyOrderedDictionary values)
        {
            List<KeyValuePair<string, object>> _keys = new List<KeyValuePair<string, object>>(),
                _values = new List<KeyValuePair<string, object>>();
            foreach (var field in this.EntityFieldInfos)
            {
                if (field.Value.IsPrimaryField == true)
                    _keys.Add(new KeyValuePair<string, object>(field.Key, this[field.Key]));
                _values.Add(new KeyValuePair<string, object>(field.Key, this[field.Key]));
            }

            keys = new ReadonlyOrderedDictionary(_keys);
            values = new ReadonlyOrderedDictionary(_values);
        }
        #endregion

        #region ISubmitable 成员

        /// <summary>
        /// 插入实体
        /// </summary>
        /// <returns>刚刚插入的ID，如果主键不是自增长则返回-1</returns>
        public override int Create()
        {
            //获取事件参数
            ReadonlyOrderedDictionary keys, values;
            this.GetEntityKeyValues(out keys, out values);

            //检查Before事件
            DataQuery query = this.mapperCommand.GetInsertCommand();
            BeforeSubmitEvent beforeCmd = new BeforeSubmitEvent("BeforeSubmitCommand_" + MapperEvent.EntityCreate, query, keys, values);
            eventBus.Send(beforeCmd);
            if (beforeCmd.Cancel)
                return -1;
            //检查After事件
            int lastIdentity = -1;
            Exception handledException = null;
            try
            {
                lastIdentity = this.UpdateIdentity(DataProvider.GetInstance().ExecuteScalar(query));
                AfterReturningEvent returningCmd = new AfterReturningEvent("AfterReturningCommand_" + MapperEvent.EntityCreate, lastIdentity == -1 ? 0 : 1, keys, values, handledException);
                eventBus.Send(returningCmd);
            }
            catch (Exception e)
            {
                handledException = e;
                AfterThrowingEvent throwingCommand = new AfterThrowingEvent("AfterThrowingCommand_" + MapperEvent.EntityCreate, lastIdentity == -1 ? 0 : 1, keys, values, handledException);
                eventBus.Send(throwingCommand);
                if (!eventBus.HasHandler(MapperEvent.EntityCreate.ToString()))
                    throw;
            }
            finally
            {
                AfterFinallyEvent throwingCommand = new AfterFinallyEvent("AfterFinallyCommand_" + MapperEvent.EntityCreate, lastIdentity == -1 ? 0 : 1, keys, values, handledException);
                eventBus.Send(throwingCommand);
            }

            return lastIdentity;
        }
        /// <summary>
        /// 更新实体，使用主键值作为条件，其他字段值作为参数
        /// </summary>
        /// <returns></returns>
        public override bool Update()
        {
            //获取事件参数
            ReadonlyOrderedDictionary keys, values;
            this.GetEntityKeyValues(out keys, out values);

            //检查Before事件
            DataQuery query = this.mapperCommand.GetUpdateCommand();
            BeforeSubmitEvent beforeCmd = new BeforeSubmitEvent("BeforeSubmitCommand_" + MapperEvent.EntityUpdate, query, keys, values);
            eventBus.Send(beforeCmd);
            if (beforeCmd.Cancel)
                return false;
            //检查After事件
            int affectedRows = 0;
            Exception handledException = null;
            try
            {
                affectedRows = DataProvider.GetInstance().ExecuteNonQuery(query);
                AfterReturningEvent returningCmd = new AfterReturningEvent("AfterReturningCommand_" + MapperEvent.EntityUpdate, affectedRows, keys, values, handledException);
                eventBus.Send(returningCmd);
            }
            catch (Exception e)
            {
                handledException = e;
                AfterThrowingEvent throwingCommand = new AfterThrowingEvent("AfterThrowingCommand_" + MapperEvent.EntityUpdate, affectedRows, keys, values, handledException);
                eventBus.Send(throwingCommand);
                if (!eventBus.HasHandler(MapperEvent.EntityCreate.ToString()))
                    throw;
            }
            finally
            {
                AfterFinallyEvent throwingCommand = new AfterFinallyEvent("AfterFinallyCommand_" + MapperEvent.EntityUpdate, affectedRows, keys, values, handledException);
                eventBus.Send(throwingCommand);
            }
            
            return affectedRows > 0;
        }
        /// <summary>
        /// 删除实体，使用已赋值的字段作为条件
        /// </summary>
        /// <returns></returns>
        public override bool Delete()
        {
            //获取事件参数
            ReadonlyOrderedDictionary keys, values;
            this.GetEntityKeyValues(out keys, out values);

            //检查Before事件
            DataQuery query = this.mapperCommand.GetDeleteCommand();
            BeforeSubmitEvent beforeCmd = new BeforeSubmitEvent("BeforeSubmitCommand_" + MapperEvent.EntityDelete, query, keys, values);
            eventBus.Send(beforeCmd);
            if (beforeCmd.Cancel)
                return false;
            //检查After事件
            int affectedRows = 0;
            Exception handledException = null;
            try
            {
                affectedRows = DataProvider.GetInstance().ExecuteNonQuery(query);
                AfterReturningEvent returningCmd = new AfterReturningEvent("AfterReturningCommand_" + MapperEvent.EntityDelete, affectedRows, keys, values, handledException);
                eventBus.Send(returningCmd);
            }
            catch (Exception e)
            {
                handledException = e;
                AfterThrowingEvent throwingCommand = new AfterThrowingEvent("AfterThrowingCommand_" + MapperEvent.EntityDelete, affectedRows, keys, values, handledException);
                eventBus.Send(throwingCommand);
                if (!eventBus.HasHandler(MapperEvent.EntityCreate.ToString()))
                    throw;
            }
            finally
            {
                AfterFinallyEvent throwingCommand = new AfterFinallyEvent("AfterFinallyCommand_" + MapperEvent.EntityDelete, affectedRows, keys, values, handledException);
                eventBus.Send(throwingCommand);
            }
            return affectedRows > 0;
        }        
        #endregion

    }

    /// <summary>
    /// 支持事件的实体映射器工厂
    /// </summary>
    public abstract class AspectMapper : MapperBase
    {
        /// <summary>
        /// 创建指定实体的映射器
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <returns></returns>
        public static IAspectMapper<T> Inject<T>() where T : new()
        {
            return new AspectMapper<T>(default(T), null);
        }
        /// <summary>
        /// 创建指定实体的映射器
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="entity">this</param>
        /// <param name="definitionRule">实体对应映射规则</param>
        /// <returns></returns>
        public static IAspectMapper<T> Inject<T>(T entity, IEntityDefinitionRule definitionRule) where T : new()
        {
            return new AspectMapper<T>(entity, definitionRule);
        }
    }
}
